package models

import (
	"context"
	"fmt"
	"github.com/EDDYCJY/go-gin-example/pkg/app"
	"github.com/EDDYCJY/go-gin-example/pkg/e"
	"github.com/EDDYCJY/go-gin-example/pkg/setting"
	"github.com/gin-gonic/gin"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/alipay"
	"github.com/go-pay/gopay/paypal"
	"github.com/go-pay/gopay/pkg/xlog"
	"github.com/google/uuid"
	"github.com/jinzhu/gorm"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type RechargeRecord struct {
	// 充值记录id
	ID uint64 `gorm:"primary_key" json:"id"`
	// 订单id
	OrderID string `gorm:"column:order_id" json:"order_id"`
	// 支付状态
	Status int32 `gorm:"column:status" json:"status"`
	// 用户id
	UserId uint64 `gorm:"column:user_id" json:"user_id"`
	// 充值金额（法定货币）
	Amount float64 `gorm:"column:amount" json:"amount"`
	// 积分数量
	CoinAmount float64 `gorm:"column:coin_amount" json:"coin_amount"`
	// 充值时间（yyyy-mm-dd hh:mm:ss）
	RechargeTime DbTime `gorm:"column:recharge_time" json:"recharge_time"`
}

func (RechargeRecord) TableName() string {
	return "recharge_records"
}

const (
	NOT_PAY     = 0
	PAY_SUCCESS = 1
)

func GetRechargeRecordById(id int64) (*RechargeRecord, e.FResult) {
	var data RechargeRecord
	err := db.Model(&data).Debug().Where("id=? ", id).First(&data).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, e.FResult{ErrCode: e.ERROR, ErrMsg: "充值记录不存在"}
		} else {
			return nil, e.FResult_ERROR
		}
	}
	return &data, e.FResult_OK
}

func GetRechargeRecordListByUserId(userId int64) ([]RechargeRecord, e.FResult) {
	var datas []RechargeRecord
	err := db.Model(&datas).Debug().Where("user_id=? ", userId).Find(&datas).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, e.FResult_ERROR
	}
	return datas, e.FResult_OK
}

// alipay
func RechargeAlipay(user_id uint64, amount float64) e.FResult {
	// 获取当前时间戳
	now := time.Now().UTC().Unix()
	// 生成一个UUID
	uuid, _ := uuid.NewUUID()
	str_uuid := uuid.String()
	str_uuid = strings.ReplaceAll(str_uuid, "-", "")
	// 生成一个随机数
	r := rand.Intn(1000000)
	// 生成定单号
	orderNo := fmt.Sprintf("%d%s%d", now, str_uuid, r)

	var record RechargeRecord
	record.UserId = user_id
	record.OrderID = orderNo
	record.Status = NOT_PAY
	record.Amount = amount
	record.CoinAmount = amount / setting.CloudPlatformSetting.CoinPrice

	// 创建充值记录
	db := db.Begin()
	err := db.Debug().Select("user_id", "order_id", "status", "amount", "coin_amount").Create(&record).Error
	if err != nil {
		db.Rollback()
		return e.FResult_ERROR
	}

	success, pay_url := send_alipay(orderNo, amount)
	if !success {
		db.Rollback()
		return e.FResult_ERROR
	}

	db.Commit()

	return e.FResult{ErrCode: e.SUCCESS, ErrMsg: pay_url}
}

func send_alipay(order_no string, amount float64) (bool, string) {
	str_amount := strconv.FormatFloat(amount, 'f', -1, 64)

	privkey := setting.AlipaySetting.AppPrvkey
	client, err := alipay.NewClient(setting.AlipaySetting.AppID, privkey, true)
	if err != nil {
		xlog.Error("alipay err:", err)
		return false, ""
	}

	client.SetCharset("utf-8").SetSignType(alipay.RSA2)
	client.DebugSwitch = gopay.DebugSwitch(setting.AlipaySetting.DebugSwitch)
	client.SetReturnUrl(setting.AlipaySetting.ReturnUrl)
	client.SetNotifyUrl(setting.AlipaySetting.NotifyUrl)

	params := make(gopay.BodyMap)
	params.Set("subject", setting.AlipaySetting.Subject).
		Set("product_code", "FAST_INSTANT_TRADE_PAY"). //固定值
		Set("out_trade_no", order_no).                 //订单号
		Set("total_amount", str_amount)                //price

	pay_url, err := client.TradePagePay(context.Background(), params)
	if err != nil {
		xlog.Error("alipay err:", err)
		return false, ""
	}

	xlog.Debug("alipay pay_url:", pay_url)
	return true, pay_url
}

func RechargeAlipayNotify(ctx *gin.Context) {
	fmt.Println("alipay recharge notify ...")

	// 解析异步通知的参数
	notifyReq, err := alipay.ParseNotifyToBodyMap(ctx.Request) // c.Request 是 gin 框架的写法
	fmt.Println()
	if err != nil {
		xlog.Error("alipay notify error:", err)
		return
	}

	// 验证签名
	pubkey := setting.AlipaySetting.AlipayPubkey
	ok, err := alipay.VerifySign(pubkey, notifyReq)
	if !ok {
		xlog.Error("alipay notify verify sign error:", err)
		return
	}

	// 支付是否成功
	trade_status := notifyReq.GetString("trade_status")
	if trade_status != "TRADE_SUCCESS" {
		xlog.Error("alipay notify, trade_status not success:", trade_status)
		return
	}

	// 订单id
	order_no := notifyReq.GetString("out_trade_no")

	db := db.Begin()
	var recharge_record RechargeRecord
	err = db.Model(&RechargeRecord{}).Debug().
		Select("*").
		Where("order_id = ?", order_no).
		First(&recharge_record).Error
	if err != nil {
		db.Rollback()
		fmt.Println("alipay notify, sql error:", err)
		return
	}

	// 更新订单状态
	err = db.Model(&RechargeRecord{}).Debug().
		Where("order_id = ?", order_no).
		Update("status", PAY_SUCCESS).Error
	if err != nil {
		db.Rollback()
		fmt.Printf("alipay notify, sql error: %s, order_id: %s\n", err.Error(), order_no)
		return
	}

	// 增加用户积分余额
	err = db.Model(&Account{}).Debug().
		Where("id = ?", recharge_record.UserId).
		Update("coins", gorm.Expr("coins + ?", recharge_record.CoinAmount)).Error
	if err != nil {
		db.Rollback()
		fmt.Println("alipay notify, sql error:", err)
		return
	}

	db.Commit()

	ctx.String(http.StatusOK, "success")
}

func RechargeAlipaySuccess(order_id string) bool {
	var record RechargeRecord
	err := db.Model(&RechargeRecord{}).Debug().
		Select("*").
		Where("order_id = ?", order_id).
		First(&record).Error
	if err != nil {
		fmt.Println("sql error: ", err)
		return false
	}

	return record.Status == PAY_SUCCESS
}

// paypal
func RechargePaypal(ctx *gin.Context, user_id uint64, amount float64) e.FResult {
	success, pprsp := create_paypal_order(ctx, amount)
	if !success || pprsp == nil {
		xlog.Error("create paypal order error")
		return e.FResult_ERROR
	}

	var return_url string
	for i := 0; i < len(pprsp.Response.Links); i++ {
		str_ref := pprsp.Response.Links[i].Rel
		if str_ref == "approve" {
			return_url = pprsp.Response.Links[i].Href
			break
		}
	}
	xlog.Debug("paypal approval_url:", return_url)

	var record RechargeRecord
	record.UserId = user_id
	record.OrderID = pprsp.Response.Id //订单id
	record.Status = NOT_PAY
	record.Amount = amount
	record.CoinAmount = amount / setting.CloudPlatformSetting.CoinPrice

	// 创建充值记录
	db := db.Begin()
	err := db.Debug().Select("user_id", "order_id", "status", "amount", "coin_amount").Create(&record).Error
	if err != nil {
		db.Rollback()
		return e.FResult_ERROR
	}
	db.Commit()

	return e.FResult{ErrCode: e.SUCCESS, ErrMsg: return_url}
}

func create_paypal_order(ctx *gin.Context, amount float64) (bool, *paypal.CreateOrderRsp) {
	str_amount := strconv.FormatFloat(amount, 'f', -1, 64)

	client_id := setting.PaypalSetting.ClientID
	secret := setting.PaypalSetting.Secret

	client, err := paypal.NewClient(client_id, secret, true)
	if err != nil {
		xlog.Error("paypal err:", err)
		return false, nil
	}
	client.DebugSwitch = gopay.DebugSwitch(setting.PaypalSetting.DebugSwitch)

	// 获取当前时间戳
	now := time.Now().UTC().Unix()
	// 生成一个UUID
	uuid, _ := uuid.NewUUID()
	str_uuid := uuid.String()
	str_uuid = strings.ReplaceAll(str_uuid, "-", "")
	// 生成一个随机数
	r := rand.Intn(1000000)
	// 生成定单号
	random_no := fmt.Sprintf("%d%s%d", now, str_uuid, r)

	var pus []*paypal.PurchaseUnit
	var item = &paypal.PurchaseUnit{
		ReferenceId: random_no,
		Amount: &paypal.Amount{
			CurrencyCode: "USD",
			Value:        str_amount,
		},
	}
	pus = append(pus, item)

	bm := make(gopay.BodyMap)
	bm.Set("intent", "CAPTURE").
		Set("purchase_units", pus).
		SetBodyMap("application_context", func(b gopay.BodyMap) {
			b.Set("locale", "en-PT").
				Set("return_url", setting.PaypalSetting.ReturnUrl).
				Set("cancel_url", setting.PaypalSetting.CancelUrl)
		})

	ppRsp, err := client.CreateOrder(ctx, bm)
	if err != nil {
		xlog.Error("paypal err:", err)
		return false, nil
	}
	if ppRsp.Code != paypal.Success {
		xlog.Error("paypal ppRsp.Code:", ppRsp.Code)
		return false, nil
	}

	return true, ppRsp
}

type RechargePaypalCaptureReq struct {
	OrderID string `json:"order_id" binding:"required"`
}

func RechargePaypalCapture(c *gin.Context) {
	appG := app.Gin{C: c}

	var req RechargePaypalCaptureReq
	if err := c.ShouldBindJSON(&req); err != nil {
		appG.ResponseError(e.ERROR, "请求参数错误:"+err.Error())
		return
	}

	var query_recharge_record RechargeRecord
	err := db.Model(&RechargeRecord{}).Debug().
		Select("*").Where("order_id = ?", req.OrderID).
		First(&query_recharge_record).Error
	if err != nil {
		fmt.Println("paypal capture, order_id not exist")
		appG.ResponseError(e.ERROR, "order_id not exist:"+err.Error())
		return
	}
	if query_recharge_record.Status == PAY_SUCCESS {
		fmt.Println("order has pay success, order_id: ", req.OrderID)
		appG.ResponseSuccess(nil)
		return
	}

	client_id := setting.PaypalSetting.ClientID
	secret := setting.PaypalSetting.Secret

	client, err := paypal.NewClient(client_id, secret, true)
	if err != nil {
		appG.ResponseError(e.ERROR, "NewCLient失败:"+err.Error())
		return
	}
	client.DebugSwitch = gopay.DebugSwitch(setting.PaypalSetting.DebugSwitch)

	ppRsp, err := client.OrderCapture(c, req.OrderID, nil)
	if err != nil {
		appG.ResponseError(e.ERROR, "OrderCapture失败:"+err.Error())
		return
	}

	if ppRsp.Code != paypal.Success {
		appG.ResponseError(e.ERROR, "OrderCapture失败")
		return
	}

	st := ppRsp.Response.Status
	if st == "COMPLETED" {
		order_no := req.OrderID

		db := db.Begin()
		var recharge_record RechargeRecord
		err = db.Model(&RechargeRecord{}).Debug().
			Select("*").
			Where("order_id = ?", order_no).
			First(&recharge_record).Error
		if err != nil {
			db.Rollback()
			fmt.Println("paypal capture, error:", err)
			return
		}

		// 更新订单状态
		err = db.Model(&RechargeRecord{}).Debug().
			Where("order_id = ?", order_no).
			Update("status", PAY_SUCCESS).Error
		if err != nil {
			db.Rollback()
			fmt.Printf("paypal capture, sql error: %s, order_id: %s\n", err.Error(), order_no)
			return
		}

		// 增加用户积分余额
		err = db.Model(&Account{}).Debug().
			Where("id = ?", recharge_record.UserId).
			Update("coins", gorm.Expr("coins + ?", recharge_record.CoinAmount)).Error
		if err != nil {
			db.Rollback()
			fmt.Println("paypal capture, sql error:", err)
			return
		}
		db.Commit()

		fmt.Println("recharge_capture success")
		appG.ResponseSuccess(nil)
		return
	} else {
		appG.ResponseError(e.ERROR, "支付查询失败")
		return
	}
}
